using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Text;
using Iivil.Infrastructure.Collections.Generic;

namespace Iivil.Infrastructure.Collections.Generic
{
    public class Set<T> : ICollection<T> {
        private bool _isReadOnly;
        private int _count;
        private T[] _data;
        private int _capacity;
        private const int _defaultCapacity = 16;
        
        public Set() {
            _isReadOnly = false;
            _count = 0;
            _capacity = _defaultCapacity;
            _data = new T[_capacity];
        }
                
        public Set(IEnumerable<T> data) : this() {
            foreach (T t in data)
                this.Add(t);
        }
        
        public T this[int index] {
            get {
                if (index < 0 || index >= _count)
                    throw new ArgumentOutOfRangeException();
                return _data[index];
            }
        }
        
        public void Add(T item) {
            if (_isReadOnly)
                throw new ReadOnlyException("Read Only");
            // increase capacity?
            if (_count == _capacity) {
                int oldCapacity = _capacity;
                _capacity = (int)System.Math.Ceiling(((double)oldCapacity)*1.20);
                T[] temp = new T[_capacity];
                Array.Copy(_data, temp, oldCapacity);
                _data = temp;
            }

            if (!Contains(item))
                _data[_count++] = item;
        }

        public void Clear() {
            if (_isReadOnly)
                throw new Exception("Read Only");
            _capacity = _defaultCapacity;
            _data = new T[_capacity];
            _count = 0;
        }

        public bool Contains(T item) {
            bool ret = false;
            for (int i = 0; i < _count; i++) {
                if (item != null) {
                    if (item.Equals(_data[i]))
                        ret = !ret;
                }else {
                    if (_data[i] == null)
                        ret = !ret;
                }
            }
            return ret;
        }

        public void CopyTo(T[] array, int arrayIndex) {
            Array.Copy(_data, 0, array, arrayIndex, _count);
        }

        public bool Remove(T item) {
            bool remove = false;
            for (int i = 0; i < _count; i++) {
                if (item != null) {
                    if (item.Equals(_data[i]))
                        remove = true;
                } else {
                    if (_data[i] == null)
                        remove = true;
                }
                if (remove) {
                    for (int k = i; k < _count; k++) {
                        _data[k] = _data[k + 1];
                    }
                    _count--;
                    break;
                }
                    
            }
            return remove;
        }

        public int Count {
            get { return _count; }
        }

        public bool IsReadOnly {
            get { return _isReadOnly; }
        }

        public Set<T> Union(Set<T> set) {
            Set<T> ret = new Set<T>();
            foreach (T symbol in set)
                ret.Add(symbol);

            foreach (T symbol in this)
                if (!ret.Contains(symbol))
                    ret.Add(symbol);           
            
            return ret;
        }
        
        public Set<T> Intersection(Set<T> set) {
            Set<T> ret = new Set<T>();
            foreach (T t in set)
                if (this.Contains(t))
                    ret.Add(t);
            return ret;
        }
        
        public Set<T> Difference(Set<T> set) {
            Set<T> ret = new Set<T>(this);
            foreach (T t in set)
                ret.Remove(t);
            return ret;
        }


        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            return new SetEnumerator<T>(this);
        }

        public IEnumerator GetEnumerator() {
            return new SetEnumerator<T>(this);
        }
        
        public static bool operator ==(Set<T> s1, Set<T> s2) {
            if (s1.Count != s2.Count)
                return false;
            foreach (T t in s1) 
                if (!s2.Contains(t))
                    return false;
            return true;
        }

        public static bool operator !=(Set<T> s1, Set<T> s2) {
            return !(s1 == s2);
        }

        public override bool Equals(object obj) {
            if (obj is Set<T>)
                return ((Set<T>)obj) == this;
            else
                return false;
        }

        public override int GetHashCode() {
            return base.GetHashCode();
        }

        public void AddRange(T[] items)
        {
            Array.ForEach<T>(items, this.Add);
        }

        public T[] ToArray()
        {
            T[] ret = new T[_count];
            Array.Copy(_data, ret, _count);
            return ret;
        }
    }

    public class SetEnumerator<T> : IEnumerator<T> {
        private Set<T> _set;
        private int _currentPos;
        internal SetEnumerator(Set<T> set) {
            _set = set;
            _currentPos = -1;
        }
        
        T IEnumerator<T>.Current {
            get {
                return _set[_currentPos];
            }
        }

        public void Dispose() {
            _set = null;
        }

        public bool MoveNext() {
            return (++_currentPos < _set.Count);
        }

        public void Reset() {
            _currentPos = -1;
        }

        public object Current {
            get {
                return _set[_currentPos];
            }
        }
    }
}